﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Reflection;

namespace per.esmool.utils.DB {

    public class DbFieldKit<T> where T : IIDData<T> {

        public (IEnumerable<string>, IEnumerable<P>) BuildTokens<P>(Dictionary<string, object> map, Func<string, string> parameterNamer)
            where P : DbParameter, new() {
            var keys = map.Keys;
            var names = new List<string>();
            var parameters = new List<P>();
            foreach (var key in keys) {
                var value = map[key];
                var pName = parameterNamer.Invoke(key);

                names.Add(key);
                parameters.Add(new P() {
                    ParameterName = pName,
                    Value = value ?? DBNull.Value
                });
            }
            return (names, parameters);
        }

        public Dictionary<string, object> Diff(T old, T alternative) {
            var oldFields = GetAllFields(old);
            var alterFields = GetAllFields(alternative);
            var keys = oldFields.Keys;
            var map = new Dictionary<string, object>();
            foreach (var key in keys) {
                var oValue = oldFields[key];
                var aValue = alterFields[key];
                if (TypeKit.Equals(oValue, aValue))
                    continue;
                map.Add(key, aValue);
            }
            return map;
        }

        public Dictionary<string, object> GetAllFields(T obj) {
            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField | BindingFlags.SetField);
            Dictionary<string, object> map = new();
            foreach (var property in properties) {
                var attribute = property.GetCustomAttribute<DbFieldAttribute>();
                if (attribute is null)
                    continue;
                var name = attribute.Name ?? FirstLetterToLower(property.Name);
                var dbType = attribute.Type ?? property.PropertyType;

                var value = property.GetValue(obj, BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField, null, null, null);
                var dbValue = ToDBType(dbType, value);
                map[name] = dbValue;
            }
            return map;
        }

        protected string FirstLetterToLower(string text) {
            var c = text.Substring(0, 1).ToLower();
            var rest = text.Substring(1);
            return c + rest;
        }

        protected object ToDBType(Type destType, object value) {
            return TypeKit.ChangeType(destType, value);
        }

        protected object FromDBType(Type destType, object value) {
            return TypeKit.ChangeType(destType, value);
        }

    }

}
